#ifndef __ILoadableObject_H__
#define __ILoadableObject_H__
//#include "thread/ITask.h"
//#include "IrrlichtDevice.h"
//#include "thread/IMutex.h"
#include <lampe/LoadableInterface.h>
#include <lampe/thread/IMutex.h>
#include <lampe/thread/IThreadTask.h>
#include <lampe/thread/IThread.h>
namespace lampe{

/***
 * @class ILoadableObject
 * Класс, означающий, что объект его унаследувавший может динамический
 * загружаться или выгружаться. Реализует функции динамической синхронной
 * и асинхронной загрузки
 */

class ILoadableObject:public virtual LoadableInterface,public virtual irr::IReferenceCounted{
private:
     lampe::thread::IMutex*load_mutex;
public:
	enum LoadStatus{
		UnLoaded=1,
		Loaded,
		UnLoading,
		Loading

	};
private:
	LoadStatus status;

public:
	/***
	 * Загружает объект синхронно
	 */
	virtual bool load(){
		if (status==UnLoaded){
			load_mutex->lock();
			status=Loading;
			on_load();
			status=Loaded;
			load_mutex->unlock();
			return true;
		}
		if (status==Loading){
			load_mutex->wait();
			return true;
		}
		return false;
	}
	/***
	 * Выгружает объект синхронно
	 */
	virtual bool unload(){
		if (status==Loaded){
			load_mutex->lock();
			status=UnLoading;
			on_unload();
			status=UnLoaded;
			load_mutex->unlock();
			return true;
		}
		if (status==UnLoading){
			load_mutex->wait();
			return true;
		}
		return false;
	}

	/***
	 * Возвращяет статус загрузки/выгрузки
	 */
	virtual LoadStatus getLoadStatus(){
		return status;
	}

	/***
	 * Возвращяет полностью ли загружен объект
	 *
	 * @return TRUE если объкт полностью загружена, иначе FALSE
	 */
	virtual bool isLoaded(){
		return status==Loaded;
	}

	/***
	 * Возвращяет полностью ли вгыружен объект
	 *
	 * @return TRUE если объект полностью выгружена, иначе FALSE
	 */
	virtual bool isUnloaded(){
		return status==UnLoaded;
	}
private:
    //irr::IrrlichtDevice*device;
    //irr::thread::IThread*lastThread;

	
    class LoadTask{
	private:
        lampe::thread::IThreadTask*run_end_task;
		ILoadableObject*obj;
		bool load;
		
	public:

        LoadTask(ILoadableObject*o,bool l,lampe::thread::IThreadTask*r){
			obj=o;
			run_end_task=r;
			load=l;
		}

        void operator()(lampe::thread::IThread*thread) {
            obj->grab();
			if (load)
				obj->load();
			else
				obj->unload();
			obj->drop();
			if (run_end_task)
                run_end_task->operator()(thread);
                //run_end_task->run();
		}
	};
public:

	/***
	 * Возвращяет устройство движка, к которому привязан данный загрузчик
	 *
	 * @return устройство движка, к которому привязан данный загрузчик
	 */
    //irr::IrrlichtDevice*getDevice(){
    //	return device;
    //}

	/***
	 * Загружает объект асинхронно
	 *
	 * @param task задание, которое нужно выполнить после загрузки. Может быть равным NULL
	 */
    virtual void load_unsync(lampe::thread::IThreadTask*task=0){
        lampe::thread::runTask(LoadTask(this,true,task));
        //device->giveTaskAndRunThread(new LoadTask(this,true,task));
	}

	/***
	 * Выгружает объект асинхронно
	 *
	 * @param task задание, которое нужно выполнить после выгрузки. Может быть равным NULL
	 */
    virtual void unload_unsync(lampe::thread::IThreadTask*task=0){
        //device->giveTaskAndRunThread(new LoadTask(this,false,task));
        lampe::thread::runTask(LoadTask(this,false,task));
	}

	/***
	 * Конструктор
	 *
	 * @param d используемое устройство Irrlicht
	 */
	ILoadableObject(irr::IrrlichtDevice*d){
        load_mutex=lampe::thread::createRecursiveMutex();
		status=UnLoaded;
        //lastThread=0;
	}

	~ILoadableObject(){
		unload();
		load_mutex->drop();
	}
};

}//namespace lampe
#endif
